/** 坐标 */
export type Coord = [x: number, y: number]

const createGlContext = (cv: HTMLCanvasElement, options?: WebGLContextAttributes) =>
	cv.getContext('webgl2', {
		stencil: false,
		premultipliedAlpha: false,
		preserveDrawingBuffer: true,
		...options
	})!

const DRAW_ARRAY = new Float32Array([-1, -1, 3, -1, -1, 3])

/** 世界 */
export class World {
	/** 每个纹理槽的大小 */
	slotSize = 32

	/** 画布 */
	canvas
	gl
	uZoom!: WebGLUniformLocation

	private _scale!: number

	/** 获取缩放 */
	get scale() {
		return this._scale
	}
	/** 设置缩放 */
	set scale(v: number) {
		this._scale = v
		this.gl.uniform1f(this.uZoom, v)
	}

	constructor(canvas: HTMLCanvasElement) {
		this.canvas = canvas
		this.gl = createGlContext(canvas)
		this.onResize()
		addEventListener('resize', () => this.onResize())
	}

	/** 调整画布大小 */
	onResize() {
		this.canvas.width = document.body.clientWidth
		this.canvas.height = document.body.clientHeight
	}

	shader(glsl: string) {
		const gl = this.gl

		const vs = gl.createShader(gl.VERTEX_SHADER)!,
			fs = gl.createShader(gl.FRAGMENT_SHADER)!
		gl.shaderSource(vs, '#version 300 es\nin vec4 iPosition;void main(){gl_Position=iPosition;}')
		gl.shaderSource(fs,
			'#version 300 es\n' +
			'precision highp float;uniform vec3 iResolution;uniform float iTime;'
			+ glsl)
		gl.compileShader(vs)
		gl.compileShader(fs)
		const pr = gl.createProgram()!
		const uniform = (name: string) => gl.getUniformLocation(pr, name)!
		gl.attachShader(pr, vs)
		gl.attachShader(pr, fs)
		gl.linkProgram(pr)
		let log: string | null
		if (!gl.getProgramParameter(pr, gl.LINK_STATUS)) {
			if (!gl.getShaderParameter(vs, gl.COMPILE_STATUS)) // vs error
				log = gl.getShaderInfoLog(vs)
			else if (!gl.getShaderParameter(fs, gl.COMPILE_STATUS)) // fs error
				log = gl.getShaderInfoLog(fs)
			else // link error
				log = gl.getProgramInfoLog(pr)
			gl.deleteProgram(pr)
			throw new Error(log!)
		}
		const mVBO_Tri = gl.createBuffer()
		gl.bindBuffer(gl.ARRAY_BUFFER, mVBO_Tri)
		gl.bufferData(gl.ARRAY_BUFFER, DRAW_ARRAY, gl.STATIC_DRAW)
		gl.bindBuffer(gl.ARRAY_BUFFER, null)
		gl.useProgram(pr)
		addEventListener('unload', () => {
			gl.useProgram(null)
			gl.deleteProgram(pr)
		})

		this.uZoom = uniform('zoom')
		const uTime = uniform('iTime'),
			uRes = uniform('iResolution'),
			canvas = this.canvas,
			update = () => {
				gl.uniform1f(uTime, performance.now() / 1e3)
				gl.viewport(0, 0, canvas.width, canvas.height)
				gl.uniform3f(uRes, canvas.width, canvas.height, 1)
				const vpos = gl.getAttribLocation(pr, 'iPosition')
				gl.bindBuffer(gl.ARRAY_BUFFER, mVBO_Tri)
				gl.vertexAttribPointer(vpos, 2, gl.FLOAT, false, 0, 0)
				gl.enableVertexAttribArray(vpos)
				gl.drawArrays(gl.TRIANGLES, 0, 3)
				gl.disableVertexAttribArray(vpos)
				gl.bindBuffer(gl.ARRAY_BUFFER, null)
				requestAnimationFrame(update)
			}
		update()
		return pr
	}
}